<?

/**
 * $Id: Router.mod.php 394 2008-05-28 19:28:03Z dakl $
 * Router przetwarzajacy url na parametry
 *
 */
class Router {

	public static $ACT_URL = '__act_url_*&54__';
	public static $PREV_URL = '__prev_url_*&54__';

	/**
	 * Zdefiniowane urle
	 *
	 * @var array
	 */
	static public $route = array();
	/**
	 * Zdefiniowane przekierowania
	 * @var array
	 */
	static public $redirect = array();
	/**
	 * Delimiter w urlach
	 *
	 * @var unknown_type
	 */
	static private $delimiter = URL_DELIMITER;
	/**
	 * Elementy stale do usuniecia z urli
	 *
	 * @var unknown_type
	 */
	static private $cutoff = array(APPLICATION_FILE_TYPE);
	/**
	 * Kontroler
	 *
	 * @var unknown_type
	 */
	static private $controller;
	/**
	 * Metoda
	 *
	 * @var unknown_type
	 */
	static private $method;
	/**
	 * Parametry
	 *
	 * @var unknown_type
	 */
	static private $param = array();
	/**
	 * Konfiguracja kontrolera
	 *
	 * @var unknown_type
	 */
	static private $config;
	/**
	 * Parametr odpowadajacy za zmuszenie kontrolera, by przepuszczal tylko zdefiniowane i zwalidowane urle
	 *
	 * @var unknown_type
	 */
	static public $forceExactRoute = false;
	/**
	 * Parametr odpowadajacy za odwrocenie tablicy z prametrami (akcje na poczatku a idki na koncu)
	 *
	 * @var boolean
	 */
	static public $reverseRoute = false;
	/**
	 * Parametr odpowadajacy za wycinanie z parama nazwy kontrolera i metody
	 *
	 * @var boolean
	 */
	static public $controllerMethodSeek = true;
	/**
	 * Parametr odpowadajacy za wycinanie z parama ostatniego pola gdy ilość jest nieparzysta
	 *
	 * @var boolean
	 */
	static public $popParamUnpaired = true;

	static public $xssRemover = false;

	static private $serializedParam = null;

	static public $curLabel = null;

	static public $curLang = 'pl'; //Dla zgodności tylko

	static public $arrayLang = array('pl' => 'pl', 'en' => 'en'); // Wykorzystywana w słowniku w adminie

	/**
	 * Pusty konstruktor
	 *
	 */
	public function __construct() {

	}

	public static function addArrayLang(array $lang) {
		self::$arrayLang[] = $lang;
	}

	public static function setArrayLang(array $lang) {
		self::$arrayLang = $lang;
	}

	public static function getArrayLang() {
		return self::$arrayLang;
	}

	public function GenerateDbRoutes() {

	}

	/**
	 * Laduje tablice sciezek z pliku
	 *
	 * @return array
	 */
	static private function GetCacheRoutes() {
		if(is_file('Router.cache')) {
			$array = unserialize(file_get_contents('Router.cache'));

			return $array;
		} else {
			return array();
		}
	}

	/**
	 * Parsuje url
	 *
	 * @return unknown
	 */
	static public function ParseUri() {
		//$_SERVER['REQUEST_URI'] = self::GenerateUrl('articleDetails',array('id' => 1,'title' => 'title'),'');
		if(isset($_SERVER['REQUEST_URI'])) {
			if(self::$xssRemover) {
				$preParsed = str_replace(URL_MAIN, "", 'http://'.$_SERVER['HTTP_HOST'].Request::CleanXss($_SERVER['REQUEST_URI']));
			} else {
				$preParsed = str_replace(URL_MAIN, "", 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
			}

			//$parsed = explode('/', $preParsed);
			//echo $preParsed;
			//$preParsed = str_replace(URL_MAIN, "", $preParsed);
			$preParsed = str_replace(self::$delimiter, '|', $preParsed);


			//$parsed=str_replace(str_replace('/index.php', '', urlencode($_SERVER['PHP_SELF'])), '', urlencode($preParsed));

			$parsed = str_replace('index.php', '', str_replace(self::$delimiter, '|',$preParsed));

			//var_dump($parsed);
			//var_dump($preParsed);

			if($parsed!='|') {
				//$parsed = str_replace($parsed, '', $preParsed);
			} else {
				//if($preParsed{0} == '|')
					//$parsed = substr($preParsed, 1,strlen($preParsed));
				//else
					//$parsed = $preParsed;
			}

			

			if(is_array($parsed)) {
				$parsed = end($parsed);
			}

			$parsed = str_replace('|', self::$delimiter, $parsed);

			foreach(self::$cutoff as $cutoff) {
				$parsedArray = explode($cutoff, $parsed);
				$parsed = $parsedArray[0];
			}

			$request = explode(self::$delimiter, $parsed);

			if(self::$reverseRoute==true) {
				$request = array_reverse($request);
			}

			//print_r($request);die();
			return $request;
		} else {
			return false;
		}
	}

	/**
	 * Pobiera parametry z urla
	 *
	 * @return unknown
	 */
	static public function GetParam() {

		$dbRouting = self::GetCacheRoutes();
		foreach($dbRouting as $route) {
			self::AddRoute($route['uri'], $route['param']);
		}

		$param = self::ParseUri();

		if(isset($param) && $param != false) {
			self::ParseRoute($param);
			self::CheckRedirecting();
			self::CheckRouting();
			//self::SaveUrl(self::$param);
			//var_dump(self::$param);
			if(is_array(self::$serializedParam)) {
				self::$param = array_merge(self::$param, self::$serializedParam);
			}

			self::$param['urlParam'] = implode(",", $param);
			//print_r(array('controller'=>self::$controller, 'method'=>self::$method, 'param'=>self::$param, 'config'=>self::$config));
			//print_r($param);
//var_dump(self::$controller);die();
			return array('controller'=>self::$controller, 'method'=>self::$method, 'param'=>self::$param, 'config'=>self::$config);
		} else {
			return false;
		}
	}

	/**
	 * Dodaje url do zdefiniowanych
	 *
	 * @param unknown_type $uri
	 * @param unknown_type $controller
	 * @param unknown_type $method
	 * @param unknown_type $paramMatrix
	 * @param unknown_type $config
	 */
	static public function AddRoute($label, $uri, $param) {
		$controller = null;
		$method = null;
		$paramMatrix = null;
		$config = null;
		$paramPush = null;
		$paramPaged = null;
		$paramSorted = null;

		/*
		 * po jakiej nazwie tabeli ma szukać w mf_route.table_src
		 */
		$paramTableSrc = null;
		/*
		 * jakie pole odpowiada za podstawienie w mf_route.id_src
		 */
		$paramIdSrc = null;

		
		if(isset($param['controller'])) {
			$controller = $param['controller'];
		}
		if(isset($param['method'])) {
			$method= $param['method'];
		}
		if(isset($param['matrix'])) {
			$paramMatrix = $param['matrix'];
		}
		if(isset($param['config'])) {
			$config = $param['config'];
		}
		if(isset($param['push'])) {
			$paramPush = $param['push'];
		}

		if(isset($param['paged'])) {
			$paramPaged = $param['paged'];
		}
		if(isset($param['sorted'])) {
			$paramSorted = $param['sorted'];
		}

		if(isset($param['table_src'])) {
			$paramTableSrc = $param['table_src'];
		}
		if(isset($param['id_src'])) {
			$paramIdSrc = $$param['id_src'];
		}

		$matchUriArray = self::UriSerializer($uri, $paramMatrix);
//print_R($matchUriArray);


		if(!isset(self::$route[$label])) {
			self::$route[$label] = array('uri'=>$uri, 'matchUri'=>$matchUriArray[0], 'controller'=>$controller, 'method'=>$method, 'paramMatrix'=>$paramMatrix, 'config'=>$config, 'param'=>$paramPush, 'variables'=>$matchUriArray[1], 'paged'=> $paramPaged, 'sorted' => $paramSorted, 'table_src' => $paramTableSrc, 'id_src' => $paramIdSrc);
		} else {
			throw new CoreException('Label alredy defined in routes');
		}
	}

	/**
	 * Dodaje przekierowanie.
	 * Wykorzystyje regexpowy mechanizm.
	 * @see http://pl.php.net/manual/pl/function.preg-match.php
	 *
	 * @param $code int, kod przekierowania
	 * @param $fromUrl
	 * @param $toUrl
	 *
	 * @example
	 *
	 */
	static public function AddRedirect($code, $fromUrl, $toUrl, $assign = null) {
		self::$redirect[] = array('code' => $code, 'fromUrl' => $fromUrl, 'toUrl' => $toUrl, 'assign' => $assign);
	}

	/**
	 * Zmienia parametr $forceExactRoute
	 *
	 * @param unknown_type $param
	 */
	static public function ForceRouting($param) {
		self::$forceExactRoute = $param;
	}

	/**
	 * Wyciaga dane z urla (kontroler, metode i parametry)
	 *
	 * @param unknown_type $param
	 */
	static private function ParseRoute($param) {
			array_pop($param);
		if(self::$popParamUnpaired==true) {
			if(count($param)%3!=0) {
				array_pop($param);
			}
		}

//print_r($param);
		//na potrzeby admina
		$tempParam = $param;
		if(self::$controllerMethodSeek!=true) {
			$controller = array_pop($param);
			if($controller != '') {
				self::$controller = $controller.'Controller';
			} else {
				self::$controller = 'IndexController';
			}
			$method = array_pop($param);
			if($method != '') {
				self::$method = $method;
			} else {
				self::$method = 'Index';
			}
			//na ptrzeby admina - przywrocenie zpopowanych wartosci w przypadku gdy do URLA w dosi jest doklejone cos
			$param = $tempParam;
		}

		$x=0;

		if(self::$reverseRoute==true) {
			$param = array_reverse($param);
		}

		foreach($param as $value) {
			if($x==0) {
				$x++;
				$key = $value;
			} else {
				self::$param[$key] = $value;
				$x=0;
			}

		}
		//Utils::ArrayDisplay($param);
		//self::$method = ucfirst(self::$param['m']);
		//unset(self::$param['m']);

	}

	/**
	 * Serializuje podany url <- experimental
	 *
	 * @param string $uri
	 * @return string
	 */
	static private function UriSerializer($uri, $paramMatrix) {

		$matchUri = $uri;
		$variables = array();
		$itemCount = 0;

			foreach(self::$cutoff as $cutoff) {
				$parsedArray = explode($cutoff, $uri);
				$parsed = $parsedArray[0];
			}

		$uriArray = array_filter(explode(self::$delimiter, $parsed), create_function('$x', 'return ($x != "");'));

		foreach($uriArray as $item) {

			if($item{0} == ':') {

				$itemName = str_replace(":", "", $item);
				$variables[$itemCount] = $itemName;
				if(isset($paramMatrix[$itemName])) {
					$item = $paramMatrix[$itemName];

				} else {
					$item = "(?P<$itemName>[^" . self::$delimiter . "]+)";

				}
			}

			if(isset($itemName)) {

				$matchUri = str_replace(":".$itemName, $item, $matchUri);

			}
			$itemName = null;
			$itemCount++;
		}

		if(!isset($matchUri)) {
			$matchUri = $uri;
		}


		return array($matchUri, $variables);
	}

	/**
	 * Weryfikuje url ze zdefiniowanymi, parsuje parametry
	 *
	 * @return true/false (pasuj�cy/niepasuj�cy)
	 */
	static private function CheckRouting() {

		foreach(self::$route as $label=>$row) {
			$notMatched = false;
			
			if(self::$forceExactRoute == true) {
				self::$method = null;
				self::$controller = null;
			}
//echo str_replace(URL_MAIN, "", 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']).'-->'.$row['matchUri'].'<br />';
//die();
			$parsed = str_replace(URL_MAIN, "/", 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']).'/';
foreach(self::$cutoff as $cutoff) {
				$parsedArray = explode($cutoff, $parsed);
				$parsed = $parsedArray[0];
			}

			if(preg_match('('.$row['matchUri'].')', $parsed, $varMatches)) {
				//echo"aaaa";
				//die();
				//var_dump($varMatches);die();
				if(is_array($row['paramMatrix'])) {
					$serializedParam = self::$param;
					if(is_array($row['variables'])) {

						foreach($row['variables'] as $varName) {
							if(isset($varMatches[$varName])) {
								self::$serializedParam[$varName]=$varMatches[$varName];
							}
							//	self::$param[$pkey] = $pvalue;
						}


					}
					
//					var_dump($parsed);
//var_dump($serializedParam);
//die();
					foreach($row['paramMatrix']['validate'] as $type => $condition) {
						$validator = new Validator(array('param'=>self::$serializedParam[$type]));
						$validator->AddValidator('param', $condition, '');
						$validator->Validate();
						if($validator->IsError()) {

							//if(!preg_match($condition, self::$param[$type])) {
							$notMatched=true;
							//}
						}
					}
				}

				if(!$notMatched) {
					if($row['controller'] != '') {
						self::$controller = $row['controller'];
					}
					if($row['method'] != '') {
						self::$method = $row['method'];
					}
					if($row['config'] != '') {
						self::$config = $row['config'];
					}
					if(is_array($row['param'])) {
						foreach($row['param'] as $pkey => $pvalue) {
							self::$param[$pkey] = $pvalue;
						}
					}//var_dump($row['variables']);var_dump(self::$serializedParam);var_dump($varMatches);
					if(is_array($row['variables'])) {

						foreach($row['variables'] as $varName) {
							if(isset($varMatches[$varName])) {
								self::$serializedParam[$varName]=$varMatches[$varName];
							}
							//	self::$param[$pkey] = $pvalue;
						}

					}

					self::$curLabel = $label;

					return true;
				} else {
					self::$method = null;
					self::$controller = null;
				}

				return false;
			}
		}

		return false;
	}


	/**
	 * Weryfikuje url z przekierowaniami
	 *
	 * @author Krku
	 */
	private static function CheckRedirecting() {
		foreach (self::$redirect as $redirect) {
			if (preg_match($redirect['fromUrl'], $_SERVER['REQUEST_URI'], $redirectVars)) {
				$newUrl = $redirect['toUrl'];

				/* Kawa�ek kodu pozwalaj�cy na uzyskanie takich rzeczy jak np. tytu� artyku�u podczas gdy nie jest on dost�pny
				 * z stargo url'a a w nowym powinien (jesli nie musi:P) wyst�powa�.
				*/
				if ($redirect['assign']) {
					foreach ($redirect['assign'] as $name => $value) {
						if (is_array($value)) {
							if (isset($value['eval'])) {
								foreach ($redirectVars as $rk => $rv) {
									$value['eval'] = str_replace("\$$rk", $rv, $value['eval']);
								}

								$redirectVars[$name] = eval($value['eval']);
							} else if (isset($value['static'])) {
								$redirectVars[$name] = $value;
							}
						} else {
							$redirectVars[$name] = $value;
						}
					}
				}

				foreach ($redirectVars as $rk => $rv) {
					$newUrl = str_replace("\$$rk", $rv, $newUrl);
				}

				$fDoBreak = false;
				switch ($redirect['code']) {
					case 301:
						$description = 'HTTP/1.1 301 Moved Permanently';
						break;
					case 302:
						$description = 'HTTP/1.1 301 Found';
						break;
					default:
						$fDoBreak = true;
				}

				if ($fDoBreak) {
					break;
				}

				header($description);
				header('Location: ' . Config::Get('URL_MAIN') . $newUrl);
				header('Connection: close');
				die();
			}
		}
	}

	/**
	 * Funkcja generuje url.
	 *
	 * @param <string> $label
	 * @param <array> $param
	 * @param <string> $where
	 * @return <string>
	 */
	static public function GenerateUrl($label = null, $param = null, $where = URL_MAIN) {


            
		if ($label == null && $param == null) {
			return URL_MAIN;
		}

		if($label == "current") {
			if (isset($param['label'])) {
				unset($param['label']);
			}

			if (self::$curLabel && self::$curLabel != "current") {
				$label = self::$curLabel;
				$current = true;
			} else {
				$label = null;
			}
		} else {
			$current = false;
		}

		if ($current) {
			if (!is_array($param)) {
				$param = self::$param;
			} else {
				$param = $param + self::$param;
			}
		}
                
		if(isset($label) && !is_null($label) && isset(self::$route[$label])) {
			$url = self::$route[$label]['uri'];
			if(is_array(self::$route[$label]['variables'])) {

				$tempVariables = self::$route[$label]['variables'];

				if(isset(self::$route[$label]['sorted'])) {
					if (isset($param[ROUTER_DIR_LABEL])) {
						$tempVariables[] = ROUTER_DIR_LABEL;
						$url = ROUTER_DIR_LABEL . self::$delimiter . ':' .ROUTER_DIR_LABEL . self::$delimiter .$url;
					}
					if (isset($param[ROUTER_SORT_LABEL])) {
						$tempVariables[] = ROUTER_SORT_LABEL;
						$url = ROUTER_SORT_LABEL . self::$delimiter . ':' .ROUTER_SORT_LABEL . self::$delimiter .$url;
					}
				}

				if(isset(self::$route[$label]['paged'])) {
					if (isset($param[ROUTER_PAGE_LABEL])) {
						$tempVariables[] = ROUTER_PAGE_LABEL;
						$url = ROUTER_PAGE_LABEL . self::$delimiter . ':' .ROUTER_PAGE_LABEL . self::$delimiter .$url;
					}
				}


				foreach($tempVariables as $varName) {
					if(isset($param[$varName])) {
						$url = str_replace(':'.$varName, $param[$varName], $url);
					} else {
						throw new UserException('Mising param: '.$varName);
					}
				}
			}
                       $return = $where . $url;
					  if($return!='') {
					$parentExtension = Config::Get('APPLICATION_FILE_TYPE');
				} else {
					$parentExtension = null;
				}
			return $return. (isset($param['_extension']) ? '.' . $param['_extension'] : $parentExtension);

		} else {

			$returnAdd = null;

			$firstFlag = true;

			if ($param == null) {
				return Config::Get('URL_MAIN');
			}
                       
			if(isset($param) && is_array($param)) {

				if(isset($param['_current'])) {
					$tempParam = explode(',', self::$param['urlParam']);

					$oldParam = $param;
					$param = array();

					for ($i = sizeof($tempParam) - 2; $i >= 0 ; $i -= 2) {
						if (self::$reverseRoute)
							$param[$tempParam[$i+1]] = $tempParam[$i];
						else
							$param[$tempParam[$i]] = $tempParam[$i+1];
					}

					$firstFlag = false;

					$param = array_merge($param, $oldParam);

					unset($param['_current']);
				}

				$urlArray = array();

				if (isset($param['_value'])) {//Stara wersja

					$return = explode(',', $param['_value']);

					$controller = array_shift($return);
					$model = array_shift($return);

					if (!empty($model)) {
						$return[] = $model;
					}
					$return[] = $controller;

					unset($param['_value']);

					$returnAdd = implode(self::$delimiter, $return);
				}

				if(isset($param[0]) && count(array_values($param))==1) {
					$urlArray = $param;
				} else {
                                       
					if (isset($param['_controller'])) {
						$firstFlag = false;
						$urlArray = array_merge(array((isset($param['_action']) ? $param['_action'] : "Index") . URL_DELIMITER . $param['_controller']), $urlArray);
					}

					foreach ($param as $k => $v)
                                        {
                                                if($k=='prev' || $k=='next') continue;
						if (substr($k, 0, 1) == '_') {
							continue;
						}

						if ($v === null) {
							continue;
						}

						if (in_array($k, $urlArray)) {
							$urlArray[$k] = $v;
						}

						if ($v !== false) {
							if ($firstFlag && self::$controllerMethodSeek==true) {
								$imp = $v . URL_DELIMITER . $k;

								$firstFlag = false;
							} else {
								$imp = $k . URL_DELIMITER . $v;
							}
						} else {
							$firstFlag = false;
							$imp = $k;
						}
                                               
						$urlArray = array_merge(array($imp), $urlArray);
                                              
                                                
                                                
					}
				}
                                 
                                
				//reakcja na ustawienia routera
				if(self::$reverseRoute==true) {
					$urlArray=array_reverse($urlArray);
				}
                               
				if($returnAdd!='') {

					$result = implode(self::$delimiter, $urlArray);
					if($result!='') {
						$return = $returnAdd.self::$delimiter.$result;
					} else {
						$return = $returnAdd;

					}

				} else {
                                       
					$return = implode(self::$delimiter, $urlArray);
				}
                                
			} else {
				$current = self::$param['urlParam'];
				$current = str_replace(',', self::$delimiter, $current);
				$return = $current;
			}

                        
			if($where  == false) {
				return $return . (isset($param['_extension']) ? '.' . $param['_extension'] : Config::Get('APPLICATION_FILE_TYPE'));
			} else {

                           
				if($return!='') {
					$parentExtension = Config::Get('APPLICATION_FILE_TYPE');
				} else {
					$parentExtension = null;
				}
				return $where . '/' . $return . (isset($param['_extension']) ? '.' . $param['_extension'] : $parentExtension);
			}
		}
	}

	static public function SaveUrl($param = array()) {
		if(is_array($param) && key_exists('urlParam', $param) && count($param) == 1) {
			$param[$param['urlParam']] = null;
			unset($param['urlParam']);
		}
		$la = SessionProxy::GetValue(self::$ACT_URL);
		SessionProxy::SetValue(self::$PREV_URL, $la);
		SessionProxy::SetValue(self::$ACT_URL, $param);
	}

	static public function DeleteThisUrl() {
		$la = SessionProxy::GetValue(self::$PREV_URL);
		SessionProxy::SetValue(self::$ACT_URL, $la);
	}

	static public function CleanUrlHistory() {
		SessionProxy::SetValue(self::$PREV_URL, array());
		SessionProxy::SetValue(self::$ACT_URL, array());
	}
}

